// 多态
//在继承关系中，子类如果定义了一个与父类方法签名完全相同的方法，被称为覆写（Override）。

//例如，在Person类中，我们定义了run()方法：
class Person {
	public void run() {
		System.out.println("Person.run");
	}
}

//在子类Student中,覆写这个run()方法:
class Student extends Person {
	@Override
	public void run() {
		System.out.println("Student.run");
	}
}

//Override和Overload不同的是,如果方法签名如果不同,就是Overload
//Overload：就是一个新方法;
//Override：方法名和父类相同,并且返回值也相同,就是Override
class Person {
    public void run() { … }
}

class Student extends Person {
    // 不是Override，因为参数不同:
    public void run(String s) { … }
    // 不是Override，因为返回值不同:
    public int run() { … }
}
// 注意：方法名相同，方法参数相同，但方法返回值不同，也是不同的方法。在Java程序中，出现这种情况，编译器会报错。


//例2
class Person {
	public void run() {...}
}

class Student extends Person {
	//不是Override,是因为‘参数’不同
	public void run(String s) {...}
	//不是Override,因为‘返回值’不同
	public int run() {...}
}

//加上@Override可以让编译器帮助检查是否进行了正确的覆写。希望进行覆写，但是不小心写错了方法签名，编译器会报错。

// override
public class Main {
    public static void main(String[] args) {
    }
}

class Person {
    public void run() {}
}

public class Student extends Person {
    @Override // Compile error!
    public void run(String s) {}
}

//但是@Override不是必需的。

//在上一节中，我们已经知道，引用变量的声明类型可能与其实际类型不符，例如：
Person p = new Student();


//现在,我们考虑一种情况,子类 覆写 父类的方法
public class Main {
    public static void main(String[] args) {
        Person p = new Student();
        p.run(); // 应该打印Person.run还是Student.run?
        //Student.run
    }
}

class Person {
    public void run() {
        System.out.println("Person.run");
    }
}

class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}


//那么，一个实际类型为Student，引用类型为Person的变量，调用其run()方法，调用的是Person还是Student的run()方法？

//运行一下上面的代码就可以知道，实际上调用的方法是Student的run()方法。因此可得出结论：
//Java的实例方法调用是基于运行时的'实际类型'的动态调用，而非变量的声明类型。
//这个非常重要的特性在面向对象编程中称之为多态。它的英文拼写非常复杂：Polymorphic。



// 多态
//多态是指，针对某个类型的方法调用，其真正执行的方法取决于运行时期实际类型的方法。例如：
Person p = new Student();
p.run(); // 无法确定运行时究竟调用哪个run()方法

//有童鞋会问，从上面的代码一看就明白，肯定调用的是Student的run()方法啊。

//但是，假设我们编写这样一个方法：
public void runTwice(Person) {
	p.run();
	p.run();
}
//它传入的参数类型是Person，我们是无法知道传入的参数实际类型究竟是Person，还是Student，还是Person的其他子类，因此，也无法确定调用的是不是Person类定义的run()方法。

//所以，多态的特性就是，运行期才能动态决定调用的子类方法。对某个类型调用某个方法，执行的实际方法可能是某个子类的覆写方法。这种不确定性的方法调用，究竟有什么作用？

//多态の例子
//假设我们定义一种收入，需要给它报税，那么先定义一个Income类：
class Income {
	protected double income;

	public double getTax() {
		return income * 0.1; //税率10%
	}
}

//对于工资收入，可以减去一个基数，那么我们可以从Income派生出SalaryIncome，并覆写getTax()：
class Salary extends Income {
	@Override
	public double getTax() {
		if (income <= 5000) {
			return 0;
		}
		return (income - 5000) * 0.2;
	}
}

//如果你享受国务院特殊津贴，那么按照规定，可以全部免税：
class StateCouncilSpecialAllowance extends Income {
	@Override
	public double getTax() {
		return 0;
	}
}

//现在，我们要编写一个报税的财务软件，对于一个人的所有收入进行报税，可以这么写：
public class Main {
	public static void main(String[] args) {
		// 给一个有普通收入、工资收入和享受国务院特殊津贴的小伙伴算税:
        Income[] incomes = new Income[] {
            new Income(3000),
            new Salary(7500),
            new StateCouncilSpecialAllowance(15000)
        };
        System.out.println(totalTax(incomes));
	}
}

public double totalTax(Income... incomes) {
	double total = 0;

	for (Income income : incomes) {
		total = total + income.getTax();
	}
}

//Polymorphic
class Income {
	protected double income;

	public Income(double income) {
		this.income = income;
	}

	public double getTax() {
		return income * 0.1;
	}
}

class Salary extends Income {
	public Salary(double income) {
		super(income);
	}

	@Override
	public double getTax() {
		if (income <= 5000) {
			return 0;
		}
		return (income - 5000) * 0.2;
	}
}

class StateCouncilSpecialAllowance extends Income {
	public StateCouncilSpecialAllowance(double income) {
		super(income);
	}

	@override
	public double getTax() {
		return 0;
	}
}


/*
观察totalTax()方法：利用多态，totalTax()方法只需要和Income打交道，它完全不需要知道Salary和StateCouncilSpecialAllowance的存在，就可以正确计算出总的税。如果我们要新增一种稿费收入，只需要从Income派生，然后正确覆写getTax()方法就可以。把新的类型传入totalTax()，不需要修改任何代码。

可见，多态具有一个非常强大的功能，就是允许添加更多类型的子类实现功能扩展，却不需要修改基于父类的代码。
*/



/*
覆写Object方法
因为所有的class最终都继承自Object，而Object定义了几个重要的方法：
	1.toString()：把instance输出为String；
	2.equals()：判断两个instance是否逻辑相等；
	3.hashCode()：计算一个instance的哈希值。
*/

//在必要的情况下，我们可以覆写Object的这几个方法。例如：
class Person {
	protected String name;

	//显示更有意义的String
	@Override
	public String toString() {
		return "Person:name = " + name;
	}

	//比较是否相等
	@Override
	public boolean equals(Object o) {
		//当且仅当o为Person类型
		if (o instanceof Person) {
			Person p = (Person) o;
			//并且两者的name的field相同时,返回true
			return this.name.equals(p.name);
		}
		return false;
	}

	//机算hash;
	@Override
	public int hashCode() {
		return this.name.hashCode();
	}
}


//调用super
//在子类的覆写方法中，如果要调用父类的被覆写的方法，可以通过super来调用。
class Person {
	protected String name;

	public String hello() {
		return "Hello, " + name;
	}
}

class Student extends Person {
	@Override
	public String hello() {
		//调用父类的hello()方法
		return super.hello() + "!";
	}
}



// final (锁死data作用域的信号)
//继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写，可以把该方法标记为final。用final修饰的方法不能被Override：
class Person {
	protected String name;

	public final String hello() {
		return "Hello," + name;
	}
}

class Student extends Person {
	//compile error: 不允许覆写
	@Override
	public String hello() {...} //出错
}


//如果一个类不希望任何其他类继承自它，那么可以把这个类本身标记为final。用final修饰的类不能被继承：
final class Person { //不能被继承
	protected String name;
}

class Student extends Person {
	// compile error : Person不开放继承
}


//对于一个类的实例字段，同样可以用final修饰。用final修饰的字段在初始化后不能被修改。例如：
class Person {
    public final String name = "Unamed";
}

//对final字段重新赋值会报错：
Person p = new Person();
p.name = "New Name"; // compile error!


//可以在构造方法中初始化final字段：
class Person {
    public final String name;

    public Person(String name) {
        this.name = name; // OK,final可以被创建时赋值
    }
}
//这种方法更为常用，因为可以保证实例一旦创建，其final字段就不可修改。





/*
小结
子类可以覆写父类的方法（Override），覆写在子类中改变了父类方法的行为；

	1.Java的‘方法调用’总是作用于‘运行期对象的实际类型’，这种行为称为‘多态’；

	2.final修饰符有多种作用：
		1.final修饰的方法可以‘阻止被覆写’；
		2.final修饰的class可以‘阻止被继承’；
		3.final修饰的field‘必须在创建对象时初始化’，随后不可修改。
*/
